import {Injectable} from "@angular/core";
import {HttpClient, HttpErrorResponse, HttpEventType} from "@angular/common/http";
import {catchError, map} from "rxjs/operators";
import {of} from "rxjs";
import {api} from "@core/service/config/api";
import {StatusCode} from "@shared/models/StatusCode";
import {BBYUtils} from "@shared/utils/BBYUtils";
import {UploadFileTask} from "@shared/models/UploadFileTask";
import {AuthService} from "@core/service/AuthService";
import {OfflineTask} from "@shared/models/OfflineTask";
import {TestData} from "@shared/utils/TestData";
import {Result} from "@shared/models/Result";
import {NzMessageService} from "ng-zorro-antd/message";


@Injectable({
  providedIn: 'root',
})
export class FileUploadService {
  uploadList: Array<UploadFileTask> = [];
  offlineList: Array<OfflineTask> = [];
  upload_card_show: boolean = false
  MAX_CHUNKED_SIZE = 1024 * 1024 * 100
  statusMap = new Map()

  constructor(
    private message: NzMessageService,
    private httpClient: HttpClient
  ) {
    this.statusMap.set('repair', 'normal')
    this.statusMap.set('running', 'active')
    this.statusMap.set('success', 'success')//'success' | 'exception' | 'active' | 'normal';
    this.statusMap.set('fail', 'exception')
    this.loadOfflineList()
  }

  preUpload(task: UploadFileTask, callback: (res: any) => void) {
    this.httpClient.post(api.preUpload,
      {
        guid: task.guid,
        fileInfo: {
          size: task.file.size,
          md5: task.md5,
          name: task.file.name,
          lastModified: task.file.lastModified
        },
        target: task.target,
        usePath: true
      }, {headers: AuthService.getAuthHeaders()})
      .subscribe(callback, error => task.status = 'exception')
  }

  start(task: UploadFileTask) {
    let that = this;
    this.httpClient.get(`${api.uploadStatus}/${task.guid}`, {headers: AuthService.getAuthHeaders()})
      .subscribe((res: any) => {
        if (res.code != StatusCode.OK) {
          return
        }
        const blobSlice = File.prototype.slice;
        let reader = new FileReader();
        task.offset = res.data.finished
        console.log("继续上传：", task)
        reader.readAsArrayBuffer(blobSlice.call(task.file, task.offset));
        // reader.onprogress
        reader.onloadend = function () {
          // 这个事件在读取结束后，无论成功或者失败都会触发
          if (reader.error) {
            console.error("文件读取失败");
          } else {
            that.uploadTaskRun(task, reader.result)
          }
        }
      })
  }

  stop(task: UploadFileTask) {
    if (task.canceler) {
      task.canceler.unsubscribe()
    }
    task.status = 'normal'
  }

  create(files: File[], distPath: string) {
    let that = this;
    for (let file of files) {
      // this.createTask(file,distPath)
      // let guid = that.guid()+"-"+new Date().getTime();
      let task = new UploadFileTask(BBYUtils.guid(), file)
      task.target = distPath
      that.uploadList.push(task);
      BBYUtils.md5File(file, md5 => {
        task.md5 = md5;
        let reader = new FileReader();
        reader.readAsArrayBuffer(file);//这个读法是异步的
        reader.onloadend = function () {
          // 这个事件在读取结束后，无论成功或者失败都会触发
          if (reader.error) {
            console.error("文件读取失败");
          } else {
            that.preUpload(task, (res: any) => {
              task.ready = true;
              console.log('上传',res)
              if (res.code == StatusCode.OK) {// 正常上传
                console.log('准备完成：', res)
                that.uploadTaskRun(task, reader.result)
                return;
              }else if (res.code == StatusCode.OK_FAST_UPLOAD){// 秒传
                task.type='fast';
                task.status="success";
                task.progress=100;
                task.finished=task.size;
              }else{
                task.status = 'exception'
              }
            })
          }
        }
      })


    }
  }


  delTask(id: number) {
    let task = this.uploadList[id]
    BBYUtils.handelResult(
      this.httpClient.delete<Result>(api.delUpload+'/'+task.guid),
      res => {
        this.uploadList.splice(id,1)
        this.message.success('任务已删除!')
      },
      res => {
        this.message.error(res.msg)
      }
    )
  }

  private uploadTaskRun(task: UploadFileTask, data: any) {
    task.status = 'active';
    let token = localStorage.getItem('token');
    task.canceler = this.httpClient.post<any>(`${api.upload}/${task.guid}?offset=${task.offset}`, data, {
      reportProgress: true,
      headers: {
        'content-type': 'application/octet-stream',
        'token': token || ''
      },
      observe: 'events'
    }).pipe(
      map((event: any) => {
        console.log(event)
        switch (event.type) {
          case HttpEventType.UploadProgress:
            task.progress = Math.round((task.offset + event.loaded) * 100 / task.file.size);
            task.finished=task.offset + event.loaded
            console.log(task)
            break;
          case HttpEventType.Response:
            return event;
        }
      }),
      catchError((error: HttpErrorResponse) => {
        console.log(error)
        task.status = 'exception';
        return of(`Upload failed: ${task.name}`);
      })).subscribe((event: any) => {
      if (typeof (event) === 'object') {
        task.status = 'success'
      }
    });
  }

  // 以下为离线下载
  loadOfflineList() {
    // this.offlineList=TestData.offline
    BBYUtils.handelResult(
      this.httpClient.get<Result>(`${api.listOffline}`, BBYUtils.createHeadersOption()),
      res => {
        console.log(res)
        this.offlineList = res.data
        this.offlineList.forEach(task => {
          task.nzStatus = this.statusMap.get(task.status)
          if (task.status == 'success') {
            task.progress = 100;
            task.finished = task.file.size;
          } else if (task.status == 'running') {

          }
        })
      },
      res => {
        console.log(res)

      }
    )
  }

  addOffline(target: string, url: string) {
    return new Promise<void>(
      (resolve, reject) => {
        if (!url) {
          this.message.error("链接为空！")
          reject()
        }
        BBYUtils.handelResult(
          this.httpClient.post<Result>(api.addOffline, {target, url}, BBYUtils.createHeadersOption()),
          res => {
            console.log(res)
            let task: OfflineTask = res.data
            task.nzStatus = this.statusMap.get(task.status)
            this.offlineList.push(task)
            this.addOfflineFlasher(task)
            this.upload_card_show = true
            resolve()
          },
          res => {
            this.message.error(res.msg)
            reject()
          }
        )
      })
  }

  private addOfflineFlasher(task: OfflineTask) {
    let interval = setInterval(() => {
      if (task.status == 'prepare') {
        this.httpClient.get<Result>(`${api.offlineTask}?taskId=${task.id}`, BBYUtils.createHeadersOption())
          .subscribe(res => {
            if (res.code == StatusCode.OK) {
              task.status = res.data.status
              task.nzStatus = this.statusMap.get(res.data.status)
              task.file = res.data.file
            }
          })
      } else if (task.status == 'running') {
        this.httpClient.get<Result>(`${api.offlineProgress}?taskId=${task.id}`, BBYUtils.createHeadersOption())
          .subscribe(res => {
            if (res.code == StatusCode.OK) {
              task.finished = res.data;
              task.progress = res.data / (task.file.size || res.data) * 100;
              if (res.data == task.file.size) {
                task.status = 'success'
                task.nzStatus = 'success'
              }
            }
          })
      } else {
        console.log('结束', task)
        if (task.status == 'success') {
          task.progress = 100;
          task.finished = task.file.size;
        }
        clearInterval(interval)
      }
    }, 3000)
  }

  delOfflineTask(task: OfflineTask, id: number) {
    this.httpClient.delete<Result>(`${api.delOffline}?taskId=${task.id}`, BBYUtils.createHeadersOption())
      .subscribe(res => {
        if (res.code == StatusCode.OK) {
          task.status = 'fail';// 停止刷新
          this.offlineList.splice(id, 1);
        } else {
          this.message.error(res.msg)
        }
      })
  }

  retryOfflineTask(task: OfflineTask) {
    // this.httpClient.get<Result>(`${api.retryOffline}?taskId=${task.id}`, BBYUtils.createHeadersOption())
    //   .subscribe(res=>{
    //     if (res.code==StatusCode.OK){
    //       task.status='fail';// 停止刷新
    //       this.offlineList.splice(id,1);
    //     }else {
    //       this.message.error(res.msg)
    //     }
    //   })
  }
}
